package com.onpositive.data.serializer.readers;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;

import com.onpositive.data.AbstractReader;
import com.onpositive.data.serializer.writers.AbstractWriter;
import com.onpositive.data.serializer.writers.StringIterableWriter;
import com.onpositive.data.serializer.writers.StringWriter;

public class StringIterableReader extends AbstractReader<Iterable<String>> {

	public static final int DIRECT_STORE = StringWriter.DIRECT_STORE;
	public static final int INDIRECT_STORE = StringWriter.INDIRECT_STORE;

	private static final int INT_SIZE = 4;
	private static final int CHAR_SIZE = 2;

	private int sizeInBytes, storageType, size, storageSize, filesCount;

	private int[] offsets, fileOffsets, indexSequence;
	private HashMap<Integer, Object> rewrites;

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public StringIterableReader() {
		super((Class) Iterable.class);
	}

	@Override
	public AbstractWriter<Iterable<String>> getWriter() {
		return new StringIterableWriter();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public ArrayList<String> getObject(int i) {

		if (!isInitialized())
			init();
		if (rewrites!=null){
			if (rewrites.containsKey(i)){
				return (ArrayList<String>) rewrites.get(i);
			}
		}
		int fileStart = fileOffsets[i];
		int fileEnd = fileOffsets[i + 1];

		ArrayList<String> result = new ArrayList<String>();
		for (int k = fileStart; k < fileEnd; k++) {
			int j;
			if (storageType == DIRECT_STORE)
				j = k;
			else if (storageType == INDIRECT_STORE) {
				j = indexSequence[k];
				if (j == 0)
					continue;
			} else {
				// throw horrible error ;
				return null;
			}

			try {
				int valueStart = offsets[j];

				this.file.seek(collectionStart + myStart + valueStart);

				// int valueLength = offsets[j+1] - valueStart ;
				// byte bArr[] = new byte[ valueLength ] ;
				// file.read( bArr, 0, valueLength ) ;
				// ByteBuffer bBuf = ByteBuffer.wrap( bArr ) ;
				// CharBuffer cBuf = bBuf.asCharBuffer() ;
				//
				// builder.delete( 0, builder.length() ) ;
				//
				// for( int a = 0 ; a < (valueLength>>1) ; a++ )
				// builder.append( cBuf.get() ) ;
				//
				// String str = builder.toString();

				String str = file.readUTF();
				result.add(str);

			} catch (IOException e) {
				e.printStackTrace();
				return null;
			}
		}
		return result.size() != 0 ? result : null;
	}

	@Override
	protected void doInit() {

		int initialReadSize = INT_SIZE * 5;

		try {
			this.file.seek(collectionStart + myStart + INT_SIZE);// also skip
																	// writerID

			byte[] byteArr = new byte[initialReadSize];

			file.read(byteArr, 0, initialReadSize);
			ByteBuffer bBuf = ByteBuffer.wrap(byteArr);
			IntBuffer iBuf = bBuf.asIntBuffer();

			sizeInBytes = iBuf.get();
			storageType = iBuf.get();
			int fileOffsetsSize = iBuf.get();
			this.fileOffsets = new int[fileOffsetsSize];

			size = iBuf.get();

			if (storageType == DIRECT_STORE) {
				int offsetsSize = size + 1;
				offsets = new int[offsetsSize];

				byteArr = new byte[INT_SIZE * offsetsSize];
				file.read(byteArr, 0, INT_SIZE * offsetsSize);
				bBuf = ByteBuffer.wrap(byteArr);
				iBuf = bBuf.asIntBuffer();

				for (int i = 0; i < offsetsSize; i++)
					offsets[i] = iBuf.get();

				byteArr = new byte[INT_SIZE * fileOffsetsSize];
				file.read(byteArr, 0, INT_SIZE * fileOffsetsSize);
				bBuf = ByteBuffer.wrap(byteArr);
				iBuf = bBuf.asIntBuffer();

				for (int i = 0; i < fileOffsetsSize; i++)
					fileOffsets[i] = iBuf.get();

				return;
			}
			if (storageType == INDIRECT_STORE) {
				storageSize = iBuf.get();

				int offsetsSize = storageSize + 1;
				offsets = new int[offsetsSize];

				byteArr = new byte[INT_SIZE * offsetsSize];
				file.read(byteArr, 0, INT_SIZE * offsetsSize);
				bBuf = ByteBuffer.wrap(byteArr);
				iBuf = bBuf.asIntBuffer();

				for (int i = 0; i < offsetsSize; i++)
					offsets[i] = iBuf.get();

				byteArr = new byte[INT_SIZE * fileOffsetsSize];
				file.read(byteArr, 0, INT_SIZE * fileOffsetsSize);
				bBuf = ByteBuffer.wrap(byteArr);
				iBuf = bBuf.asIntBuffer();

				for (int i = 0; i < fileOffsetsSize; i++)
					fileOffsets[i] = iBuf.get();

				indexSequence = new int[size];

				byteArr = new byte[INT_SIZE * size];
				file.read(byteArr, 0, INT_SIZE * size);
				bBuf = ByteBuffer.wrap(byteArr);
				iBuf = bBuf.asIntBuffer();

				for (int i = 0; i < size; i++)
					indexSequence[i] = iBuf.get();

				return;
			}

			// here we throw horrible error

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void filterOut(LinkedHashSet<Object> obj, String text) {
		if (!isInitialized())
			init();
		l2:for (Iterator iterator = obj.iterator(); iterator.hasNext();) {
			Number object = (Number) iterator.next();
			int k = object.intValue();
			ArrayList<String> object2 = getObject(k);
			for (String s:object2){
				if (s.indexOf(text)!=-1){
					continue l2;
				}
			}
			iterator.remove();
		}

	}

	@Override
	public boolean accept(int op, int a, Object constraint) {
		throw new IllegalStateException();
	}

	@Override
	public Object aggregate(int op, BitSet ss) {
		return null;
	}
	@Override
	public void setValue(int index, Object value) {
		if (rewrites == null) {
			rewrites = new HashMap<Integer, Object>();

		}
		rewrites.put(index, value==null?null:value.toString());
	}

	@Override
	public void initEmpty(int dataSize) {
		setInitialized();
	}

	@Override
	public void expand(int newSize) {
		setInitialized();
	}

}
